package com.harmontronics.hanma.paas.paas.domain.app.service;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.harmontronics.hanma.paas.paas.common.PageBase;
import com.harmontronics.hanma.paas.paas.consts.NumberEnum;
import com.harmontronics.hanma.paas.paas.domain.app.entity.HmProcess;
import com.harmontronics.hanma.paas.paas.domain.app.entity.HmTask;
import com.harmontronics.hanma.paas.paas.domain.app.repository.persistence.HmProcInstReadRepository;
import com.harmontronics.hanma.paas.paas.token.ComposeTokenKit;
import com.harmontronics.hanma.paas.paas.utils.FlowableQueryUtils;
import org.flowable.engine.HistoryService;
import org.flowable.engine.ManagementService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.NativeProcessDefinitionQuery;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.identitylink.api.IdentityLinkType;
import org.flowable.identitylink.service.impl.persistence.entity.HistoricIdentityLinkEntity;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.service.history.NativeHistoricTaskInstanceQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author ldb
 */
@Service("hmProcessHistoryService")
@DS("#header.App-Unique")
public class HmProcessHistoryService {
    @Autowired
    private TaskService taskService;

    @Autowired
    protected HistoryService historyService;

    @Autowired
    private ManagementService managementService;

    @Autowired
    private HmProcessService hmProcessService;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private HmProcessRuntimeService hmProcessRuntimeService;

    @Autowired
    private HmProcInstReadRepository hmProcInstReadRepository;

    @Autowired
    private ComposeTokenKit composeTokenKit;

    public PageBase<HmTask> listDown(Long processId, int pageNo, int pageSize) {
        Long userId = composeTokenKit.getId();
        ProcessDefinition processDefinition = hmProcessService.getProcessDefinition(processId);
        NativeHistoricTaskInstanceQuery nativeQuery = historyService.createNativeHistoricTaskInstanceQuery();

        nativeQuery.sql("SELECT T.ID_,T.NAME_,T.START_TIME_,I.USER_ID_ OWNER_,T.PROC_DEF_ID_  FROM " + managementService.getTableName(HistoricTaskInstance.class) + " T INNER JOIN " +
                managementService.getTableName(HistoricIdentityLinkEntity.class) + " I ON I.PROC_INST_ID_ = T.PROC_INST_ID_ WHERE (T.ASSIGNEE_=#{userId} OR T.OWNER_=#{userId}) AND I.TYPE_=#{type} AND I.TASK_ID_ IS NULL" +
                (processDefinition == null ? "" : " AND T.PROC_DEF_ID_=#{procDefId}"))
                .parameter("type", IdentityLinkType.STARTER).parameter("userId", userId.toString());
        if (processDefinition != null) {
            nativeQuery.parameter("procDefId", processDefinition.getId());
        }

        NativeHistoricTaskInstanceQuery nativeCountQuery = historyService.createNativeHistoricTaskInstanceQuery();

        nativeCountQuery.sql("SELECT COUNT(*)  FROM " + managementService.getTableName(HistoricTaskInstance.class) + " T INNER JOIN " +
                managementService.getTableName(HistoricIdentityLinkEntity.class) + " I ON I.PROC_INST_ID_ = T.PROC_INST_ID_ WHERE (T.ASSIGNEE_=#{userId} OR T.OWNER_=#{userId}) AND I.TYPE_=#{type} AND I.TASK_ID_ IS NULL" +
                (processDefinition == null ? "" : " AND T.PROC_DEF_ID_=#{procDefId}"))
                .parameter("type", IdentityLinkType.STARTER).parameter("userId", userId.toString());
        if (processDefinition != null) {
            nativeCountQuery.parameter("procDefId", processDefinition.getId());
        }
        PageBase<HistoricTaskInstance> pageBase = FlowableQueryUtils.doHistoricTaskQuery(nativeQuery, nativeCountQuery, pageNo, pageSize);

        PageBase<HmTask> resultPageBase = new PageBase<HmTask>().copy(pageBase);
        if (pageBase.isEmpty()) {
            return resultPageBase;
        }
        List<HmTask> dataList = new ArrayList<>();
        List<ProcessDefinition> processDefinitions = hmProcessRuntimeService.getProcessDefinitions(processDefinition, pageBase.getData());

        for (HistoricTaskInstance task : pageBase.getData()) {
            HmTask hmTask = new HmTask();
            hmTask.setTaskId(task.getId());
            hmTask.setTaskName(task.getName());
            hmTask.setCreateTime(task.getStartTime());
            hmTask.setSubmitterId(task.getOwner());
            for (ProcessDefinition definition : processDefinitions) {
                if (definition.getId().equals(task.getProcessDefinitionId())) {
                    hmTask.setProcessName(definition.getName());
                }
            }
            dataList.add(hmTask);
        }
        resultPageBase.setData(dataList);
        return resultPageBase;
    }

    public List<HmProcess> getTaskDownInvolveProcess() {
        Long userId = composeTokenKit.getId();
        //历史流程记录
        NativeProcessDefinitionQuery nativeProcessDefinitionQuery = repositoryService.createNativeProcessDefinitionQuery();
        nativeProcessDefinitionQuery.sql("SELECT ID_,DEPLOYMENT_ID_,CATEGORY_ FROM " + managementService.getTableName(ProcessDefinition.class) + " WHERE ID_ IN( SELECT DISTINCT PROC_DEF_ID_ FROM " + managementService.getTableName(HistoricTaskInstance.class) + " WHERE " +
                "ASSIGNEE_ =#{userId} OR OWNER_ =#{userId})"
        ).parameter("userId", userId.toString());
        List<ProcessDefinition> processDefinitions = nativeProcessDefinitionQuery.list();
        List<String> deploymentIds = processDefinitions.stream().map(ProcessDefinition::getDeploymentId).collect(Collectors.toList());
        return hmProcessService.findByDeploymentIdIn(deploymentIds);
    }

    public List<HmProcess> getCCInvolveProcess() {
        Long userId = composeTokenKit.getId();
        //历史流程记录
        NativeProcessDefinitionQuery nativeProcessDefinitionQuery = repositoryService.createNativeProcessDefinitionQuery();
        nativeProcessDefinitionQuery.sql("SELECT ID_,DEPLOYMENT_ID_,CATEGORY_ FROM " + managementService.getTableName(ProcessDefinition.class) + " WHERE ID_ IN( " +
                "SELECT DISTINCT INST.PROC_DEF_ID_ FROM " + managementService.getTableName(HistoricProcessInstance.class) + " INST INNER JOIN HM_PROC_INST_READ R ON R.PROC_INST_ID_=INST.PROC_INST_ID_ WHERE " +
                "R.USER_ID_ =#{userId})"
        ).parameter("userId", userId.toString());
        List<ProcessDefinition> processDefinitions = nativeProcessDefinitionQuery.list();
        List<String> deploymentIds = processDefinitions.stream().map(ProcessDefinition::getDeploymentId).collect(Collectors.toList());
        return hmProcessService.findByDeploymentIdIn(deploymentIds);
    }

    public PageBase<HmTask> listTaskCC(Long processId, Boolean read, int pageNo, int pageSize) {
        Long userId = composeTokenKit.getId();
        ProcessDefinition processDefinition = hmProcessService.getProcessDefinition(processId);
        NativeHistoricTaskInstanceQuery nativeQuery = historyService.createNativeHistoricTaskInstanceQuery();
        String sql = "SELECT T.ID_,T.PROC_INST_ID_,T.NAME_,R.CREATE_DATE START_TIME_,I.USER_ID_ OWNER_,T.PROC_DEF_ID_  FROM " + managementService.getTableName(HistoricTaskInstance.class) + " T INNER JOIN " +
                managementService.getTableName(HistoricIdentityLinkEntity.class) + " I ON I.PROC_INST_ID_ = T.PROC_INST_ID_ " +
                "INNER JOIN HM_PROC_INST_READ R ON R.PROC_INST_ID_=T.PROC_INST_ID_ AND T.ID_ = R.TASK_ID " +
                "WHERE I.TYPE_=#{type} AND I.TASK_ID_ IS NULL AND R.USER_ID_=#{userId} AND R.READ=#{read}" +
                (processDefinition == null ? "" : " AND T.PROC_DEF_ID_=#{procDefId}");

        nativeQuery.sql(sql)
                .parameter("type", IdentityLinkType.STARTER).parameter("userId", userId.toString()).parameter("read", read ? NumberEnum.ONE.getNum() : NumberEnum.ZERO.getNum());
        if (processDefinition != null) {
            nativeQuery.parameter("procDefId", processDefinition.getId());
        }

        NativeHistoricTaskInstanceQuery nativeCountQuery = historyService.createNativeHistoricTaskInstanceQuery();

        nativeCountQuery.sql("SELECT COUNT(*) FROM (" +
                sql
                + ") T")
                .parameter("type", IdentityLinkType.STARTER).parameter("userId", userId.toString()).parameter("read", read ? NumberEnum.ONE.getNum() : NumberEnum.ZERO.getNum());
        ;
        if (processDefinition != null) {
            nativeCountQuery.parameter("procDefId", processDefinition.getId());
        }
        PageBase<HistoricTaskInstance> pageBase = FlowableQueryUtils.doHistoricTaskQuery(nativeQuery, nativeCountQuery, pageNo, pageSize);

        PageBase<HmTask> resultPageBase = new PageBase<HmTask>().copy(pageBase);
        if (pageBase.isEmpty()) {
            return resultPageBase;
        }
        List<HmTask> dataList = new ArrayList<>();
        List<ProcessDefinition> processDefinitions = hmProcessRuntimeService.getProcessDefinitions(processDefinition, pageBase.getData());

        for (HistoricTaskInstance task : pageBase.getData()) {
            HmTask hmTask = new HmTask();
            hmTask.setTaskId(task.getId());
            hmTask.setTaskName(task.getName());
            hmTask.setCreateTime(task.getStartTime());
            hmTask.setSubmitterId(task.getOwner());
            for (ProcessDefinition definition : processDefinitions) {
                if (definition.getId().equals(task.getProcessDefinitionId())) {
                    hmTask.setProcessName(definition.getName());
                }
            }
            dataList.add(hmTask);
        }
        resultPageBase.setData(dataList);
        return resultPageBase;
    }

    public void readCC(String taskId) {
        Long userId = composeTokenKit.getId();
        hmProcInstReadRepository.read(taskId, userId.toString());
    }
}
